<template>
  <div ref="treeSelectRef" class="tree-select">
    <el-popover :show-arrow="false"
                :visible="popoverVisible"
                :width="width"
                placement="bottom-start"
                popper-class="tree-select_popover-body"
                @show="handlePopoverShow">

      <template #reference>
        <el-input v-model="model" :placeholder="treePlaceholder" :readonly="true" @click.native="showTreePopover">
          <template #suffix>
            <el-icon :style="{ transform: popoverVisible ? 'rotate(180deg)' : 'rotate(0)', transition: 'transform .5s', }" @click="popoverVisible = !popoverVisible">
              <ArrowDown/>
            </el-icon>
          </template>
        </el-input>
      </template>

      <div ref="operateRef">
        <div v-if="title" class="el-popover__title">{{ title }}</div>
        <!-- 过滤弹窗 -->
        <el-input ref="filterRef" v-model="filterText" :prefix-icon="Search" clearable placeholder="输入关键字进行过滤" style="margin-bottom: 10px"/>

        <!-- 树形结构数据 -->
        <el-tree ref="treeRef" :accordion="accordion"
                 :data="treeData"
                 :default-expanded-keys="defaultExpandedKeys"
                 :disabled="readonly"
                 :filter-node-method="handleTreeFilterNode"
                 :highlight-current="true"
                 :node-key="treeKey"
                 :props="treeProps"
                 :style="{height: '18rem', overflow:'auto'}"
                 @current-change="handleTreeCurrentChange">
        </el-tree>

        <!-- 操作按钮 -->
        <div class="tree-select_button">
          <el-button :size="size" @click="popoverVisible = false">取 消</el-button>
          <el-button v-if="!readonly" :size="size" type="info" @click="handleClean">清 除</el-button>
          <el-button :size="size" type="primary" @click="handleChoose">确 定</el-button>
        </div>
      </div>
    </el-popover>
  </div>
</template>

<script lang="ts" setup>
import { nextTick, ref, watch } from 'vue';
import { ElTree } from 'element-plus';
import { onClickOutside } from '@vueuse/core';
import { ArrowDown, Search } from '@element-plus/icons-vue';

defineOptions({name: 'TreeSelect'});

const model = defineModel({required: true});

const props = defineProps({
  title: {
    type: String,
    default: ''
  },
  readonly: {
    type: Boolean,
    default: false
  },
  // 是否每次只打开一个同级树节点展开
  accordion: {
    type: Boolean,
    default: true
  },
  separator: {
    type: String,
    default: ' / ',
  },
  width: {
    type: String,
    default: '300px',
  },
  size: {
    type: String,
    default: 'small'
  },
  treeData: {
    type: Array,
    default: () => []
  },
  treeKey: {
    type: String,
    default: 'id'
  },
  treeProps: {
    type: Object,
    default() {
      return {label: 'name', children: 'child'}
    }
  },
  treePlaceholder: {
    type: String,
    default: '请点击选择'
  },
  showFullName: {
    type: Boolean,
    default: true
  }
});

const treeSelectRef = ref();
const popoverVisible = ref(false);
const operateRef = ref();
const treeRef = ref<InstanceType<typeof ElTree>>();
const filterRef = ref();
const filterText = ref('');
const emit = defineEmits(['prepareShow', 'update:modelValue', 'selectChange']);
const defaultExpandedKeys = ref([0]);
const cNode = ref(undefined);

watch(filterText, (val) => {
  treeRef.value?.filter(val)
});

// 显示
const showTreePopover = () => {
  if (props.readonly) return
  emit('prepareShow')
  filterText.value = ''
  popoverVisible.value = true
};

// 显示后触发
const handlePopoverShow = () => {
  nextTick(() => {
    filterRef.value?.focus();
  });
};

// 清理
const handleClean = () => {
  if (props.readonly) return
  emit('update:modelValue', undefined)
  emit('selectChange', undefined)
  defaultExpandedKeys.value = [0]
  cNode.value = undefined
  popoverVisible.value = false
};

// 确认
const handleChoose = () => {
  if (props.readonly) return

  if (cNode.value) {
    let node = cNode.value
    let key = node.key
    let name = getNodeName(node)
    emit('update:modelValue', name)
    emit('selectChange', key)
  }
  cNode.value = undefined
  popoverVisible.value = false
};

const handleTreeFilterNode = (value: string, data: any) => {
  if (!value) return true
  // 这个地方需要注意
  return data.name.includes(value)
};

const handleTreeCurrentChange = (data: any, node: any) => {
  cNode.value = node
};

const getNodeName = (node: any) => {
  if (!node) return ''
  let name = node.label
  // 是否显示完整名称
  if (props.showFullName) {
    let pNode = node.parent
    let nodeName = ''
    while (pNode != null && pNode.label !== undefined) {
      nodeName = pNode.label + (props.separator ? props.separator : '') + nodeName
      pNode = pNode.parent
    }
    name = nodeName + node.label
  }
  return name
};

const handleSetCurrentNode = (code: any) => {
  if (code == null || !props.treeData || props.treeData.length <= 0) {
    return
  }
  defaultExpandedKeys.value = [code]
  if (treeRef.value) {
    treeRef.value.setCurrentKey(code)
    let nodeData = treeRef.value.getCurrentNode()
    if (nodeData) {
      let cNode = treeRef.value.getNode(nodeData)
      let name = getNodeName(cNode)
      emit('update:modelValue', name)
    }
  }
};

// 对外提供方法
defineExpose({handleSetCurrentNode});

/**
 * 点击容器外的区域关闭弹窗 VueUse onClickOutside
 */
onClickOutside(treeSelectRef, () => (popoverVisible.value = false), {
  ignore: [operateRef, treeRef],
});
</script>

<style lang="scss" scoped>
.tree-select {
  width: 100%;

  &_popover-body {
    max-height: 30rem;
    /*overflow: auto;*/
  }

  &_button {
    text-align: right;
    padding-top: 0.6rem;
    border-top: 1px solid var(--el-border-color);
  }
}
</style>
